﻿using Sigil;
using Sigil.NonGeneric;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace GylCSharpUtils.Common.Utils {

    /// <summary>
    /// 反射工具类，用于生成某个类中字段，属性，方法的委托
    /// </summary>
    public class ReflectionTool {

        
        private ReflectionTool() { }

        /// <summary>
        /// 创建获取属性值的委托
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="pi">属性信息</param>
        /// <returns></returns>
        public static Func<object, object> PropGetDelegate(PropertyInfo pi) {
            if(pi == null) return null;
            var type = pi.DeclaringType;
            var resultType = pi.PropertyType;
            Emit<Func<object, object>> getterEmit = Emit<Func<object,object>>.NewDynamicMethod($"{type.FullName}.{pi.Name}GetterDelegate");
            getterEmit.LoadArgument(0); // 获取第一个参数，这里是要获取属性值的对象
            getterEmit.CastClass(type); // 将对象从object转为实际的类型
            getterEmit.Call(pi.GetGetMethod(nonPublic: true));  // 获取属性的Get方法并调用
            if(resultType.IsValueType) { // 如果该属性是值类型，返回前要进行装箱以适应委托声明的object类型
                getterEmit.Box(resultType); 
            }
            getterEmit.Return();        // 返回得到的数据
            // 编译委托
            Func<object, object> getter = getterEmit.CreateDelegate();
            return getter;
        }

        /// <summary>
        /// 创建设置属性值的委托
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="pi">属性信息</param>
        /// <returns></returns>
        public static Action<object, object> PropSetDelegate(PropertyInfo pi) {
            if(pi == null) return null;
            var type = pi.DeclaringType;
            var inputType = pi.PropertyType;
            // 动态生成属性获取方法的委托的Emit对象
            Emit<Action<object, object>> setterEmit = Emit<Action<object, object>>.NewDynamicMethod($"{type.FullName}.{pi.Name}SetterDelegate");
            setterEmit.LoadArgument(0);
            setterEmit.CastClass(type);
            setterEmit.LoadArgument(1);
            if(inputType.IsValueType) { // 如果该属性是值类型，设置值之前要拆箱，以对应属性或字段的类型
                setterEmit.UnboxAny(inputType);
            } else {
                setterEmit.CastClass(inputType);
            }
            setterEmit.Call(pi.GetSetMethod(nonPublic: true));
            setterEmit.Return();        // 返回得到的数据
            // 编译委托
            Action<object, object> setter = setterEmit.CreateDelegate();
            return setter;
        }
        /// <summary>
        /// 创建获取字段值的委托
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="fieldName">字段名称</param>
        /// <returns></returns>
        public static Func<object, object> FieldGetDelegate(FieldInfo fi) {
            if(fi == null) return null;
            var type = fi.DeclaringType;
            var resultType = fi.FieldType;
            // 动态生成字段获取方法的委托的Emit对象
            Emit<Func<object, object>> getterEmit = Emit<Func<object, object>>
                .NewDynamicMethod($"{type.FullName}.{fi.Name}GetterDelegate")
                .LoadArgument(0)
                .CastClass(type);
            //if(resultType.IsValueType) { getterEmit.Box(resultType); }
                getterEmit.LoadField(fi);
            if(resultType.IsValueType) { getterEmit.Box(resultType); }
            getterEmit.Return();
            // 编译委托
            Func<object, object> getter = getterEmit.CreateDelegate();
            return getter;
        }
        /// <summary>
        /// 创建设置字段值的委托
        /// </summary>
        /// <param name="type">要设置值的对象类型</param>
        /// <param name="fieldName">字段名称</param>
        /// <returns></returns>
        public static Action<object, object> FieldSetDelegate(FieldInfo fi) {
            if(fi == null) return null;
            var type = fi.DeclaringType;
            var inputType = fi.FieldType;
            // 动态生成属性获取方法的委托的Emit对象
            Emit<Action<object, object>> setterEmit = Emit<Action<object, object>>
                .NewDynamicMethod($"{type.FullName}.{fi.Name}Setter_Delegate")
                .LoadArgument(0)                        // 装载要设置值的对象
                .CastClass(type)                        // 将对象进行类型转换
                .LoadArgument(1);                        // 装载要设置的值
            if(inputType.IsValueType) { // 如果该属性是值类型，设置值之前要拆箱，以对应属性或字段的类型
                setterEmit.UnboxAny(inputType);
            } else {
                setterEmit.CastClass(inputType);        // 将值进行类型转换
            }                
            setterEmit.StoreField(fi)                         // 设置字段
                .Return();                              // 返回字段，必不可少
            // 编译委托
            Action<object, object> setter = setterEmit.CreateDelegate();
            return setter;
        }

        /// <summary>
        /// 创建获取属性值的委托
        /// </summary>
        /// <typeparam name="A">属性所在类的类型</typeparam>
        /// <typeparam name="R">属性类型</typeparam>
        /// <param name="type"></param>
        /// <param name="pi"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static Func<A, R> PropGetDelegate<A, R>(PropertyInfo pi) {
            if(pi == null) return null;
            var type = typeof(A);
            if(!pi.DeclaringType.Equals(type)) throw new Exception($"提供的Type与PropertyInfo不匹配");
            // 动态生成属性获取方法的委托的Emit对象
            Emit<Func<A, R>> getterEmit = Emit<Func<A, R>>
                .NewDynamicMethod($"{type.FullName}.{pi.Name}GetterDelegate")
                .LoadArgument(0)                            // 获取第一个参数，这里是要获取属性值的对象
                .Call(pi.GetGetMethod(nonPublic: true))     // 获取属性的Get方法并调用
                .Return();                                  // 返回取到的属性值
            // 编译委托
            Func<A, R> getter = getterEmit.CreateDelegate();
            return getter;
        }

        /// <summary>
        /// 创建设置属性值的委托
        /// </summary>
        /// <typeparam name="A">属性所在类的类型</typeparam>
        /// <typeparam name="R">属性类型</typeparam>
        /// <param name="type">类型</param>
        /// <param name="pi">属性信息</param>
        /// <returns></returns>
        public static Action<A, R> PropSetDelegate<A, R>( PropertyInfo pi) {
            if(pi == null) return null;
            var type = typeof(A);
            if(!pi.DeclaringType.Equals(type)) throw new Exception($"提供的Type与PropertyInfo不匹配");
            // 动态生成属性获取方法的委托的Emit对象
            Emit<Action<A, R>> setterEmit = Emit<Action<A, R>>
               .NewDynamicMethod($"{type.FullName}.{pi.Name}SetterDelegate")
                .LoadArgument(0)                        // 装载要设置值的对象
                .LoadArgument(1)                        // 装载要设置的值
                .CastClass(pi.PropertyType)             // 将值进行类型转换
                .Call(pi.GetSetMethod(nonPublic: true)) // 调用设置方法
                .Return();                              // 返回字段，必不可少
            // 编译委托
            Action<A, R> setter = setterEmit.CreateDelegate();
            return setter;
        }
        /// <summary>
        /// 创建获取字段值的委托
        /// </summary>
        /// <typeparam name="A">字段所在类的类型</typeparam>
        /// <typeparam name="R">字段类型</typeparam>
        /// <param name="type">类型</param>
        /// <param name="fieldName">字段名称</param>
        /// <returns></returns>
        public static Func<A, R> FieldGetDelegate<A,R>(FieldInfo fi) {
            if(fi == null) return null;
            var type = typeof(A);
            if(!fi.DeclaringType.Equals(type)) throw new Exception($"提供的Type与FieldInfo不匹配");
            // 动态生成字段获取方法的委托的Emit对象
            Emit<Func<A, R>> getterEmit = Emit<Func<A, R>>
                .NewDynamicMethod($"{type.FullName}.{fi.Name}GetterDelegate")
                .LoadArgument(0)
                .LoadField(fi)
                .Return();
            // 编译委托
            Func<A, R> getter = getterEmit.CreateDelegate();
            return getter;
        }
        /// <summary>
        /// 创建设置字段值的委托
        /// </summary>
        /// <typeparam name="A">字段所在类的类型</typeparam>
        /// <typeparam name="R">字段类型</typeparam>
        /// <param name="type">要设置值的对象类型</param>
        /// <param name="fieldName">字段名称</param>
        /// <returns></returns>
        public static Action<A, R> CreateFieldSetDelegate<A,R>(FieldInfo fi) {
            if(fi == null) return null;
            var type = typeof(A);
            if(!fi.DeclaringType.Equals(type)) throw new Exception($"提供的Type与FieldInfo不匹配");
            // 动态生成属性获取方法的委托的Emit对象
            Emit<Action<A, R>> setterEmit = Emit<Action<A, R>>
                .NewDynamicMethod($"{type.FullName}.{fi.Name}Setter_Delegate")
                .LoadArgument(0)                        // 装载要设置值的对象
                .LoadArgument(1)                        // 装载要设置的值
                .CastClass(fi.FieldType)                   // 将值进行类型转换
                .StoreField(fi)                         // 设置字段
                .Return();                              // 返回字段，必不可少
            // 编译委托
            Action<A, R> setter = setterEmit.CreateDelegate();
            return setter;
        }
    }
}
